package progress.exa32_1.demo;

import java.awt.event.ActionListener;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * ActionListener注解处理工具类
 */
public class ActionListenerInstaller {

   /**
    * 这个方法为注解生效做好了前提条件
    * 主要分为两步：1.扫描出注解修饰的方法 2.将指定方法和注解生效的对象进行绑定
    * @param obj 目标对象
    */
   public static void processAnnotations(Object obj) {
      try {
         Class<?> cl = obj.getClass();//获取目标对象的Class对象

         /*
           遍历其方法信息,处理那些被ActionListener修饰的方法
          */
         for (Method m : cl.getDeclaredMethods()) {
            //检查使用ActionListenerFor注解的方法并将其加入监听列表中
            ActionListenerFor a = m.getAnnotation(ActionListenerFor.class);
            if (a != null) {
               //获取Class中名为source注解值的Field对象
               Field f = cl.getDeclaredField(a.source());
               f.setAccessible(true);
               //将方法加入监听中
               addListener(f.get(obj), obj, m);
            }
         }
      } catch(ReflectiveOperationException e){
         e.printStackTrace();
      }
   }

   /**
    * 此方法将指定的方法和指定的组件进行绑定，已达到监听的效果
    * 注意：此方法的实现用到了jdk的动态代理机制，看不懂的同学可以学习下动态代理原理
    * @param source 属性对象
    * @param param 目标对象
    * @param m 方法对象
    * @throws ReflectiveOperationException
    */
   public static void addListener(Object source, final Object param, final Method m) throws ReflectiveOperationException {

      //定义一个前置处理器,让按钮在执行完点击事件后执行指定的方法
      InvocationHandler handler = new InvocationHandler() {

         final Method realM = m;
         final Object realProxy = param;

            public Object invoke(Object proxy, Method m1, Object[] args) throws Throwable {
               System.out.println(m1.getName());
               if(m1.getName().equals("actionPerformed")){
                  System.out.println("执行actionPerformed!!");
               }
               //这个操作就是将param参数传入并执行m方法
               return realM.invoke(realProxy);
            }
         };

      // 获取监听器代理对象
      Object listenerProxy = Proxy.newProxyInstance(null,new Class[] { ActionListener.class }, handler);

      // 获取指定组件的指定方法对象
      Method adder = source.getClass().getMethod("addActionListener", ActionListener.class);

      //给指定按钮组件加入监听器
      adder.invoke(source, listenerProxy);
   }
}
